package com.zyajac.springBootDemo.dao.Impl;

import com.zyajac.springBootDemo.dao.DaoSupport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Repository("DaoSupport")
@Scope
@Slf4j
public class DaoSupportImpl implements DaoSupport {

    @Resource
    private SqlSessionTemplate  sqlSessionTemplate;

    private final DataSource dataSource;

    public DaoSupportImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }


    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        try {
            // 加载MyBatis配置文件
            PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            // 能加载多个，所以可以配置通配符(如：classpath*:mapper/**/*.xml)
            sqlSessionFactoryBean.setMapperLocations(
                    resourcePatternResolver.getResources("classpath:mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();

        } catch (Exception e) {log.error("SqlSessionFactory create error!", e);}

        return sqlSessionFactoryBean.getObject();
    }

    @Override
    public <T, E> E select(String id, T params) {
        return sqlSessionTemplate.selectOne(id , params);
    }

    /**
     *  封装 查询 集合
     * @param id   ID
     * @param params  参数
     * @param <T>  参数
     * @param <E>  参数
     * @return 返回
     */
    @Override
    public <T, E> List<E> selectList(String id, T params) {
        return sqlSessionTemplate.selectList(id , params);
    }

    //修改
    @Override
    public <T> int update(String id, T params){
        return sqlSessionTemplate.update(id , params);
    }

    //修改集合
    @Override
    public <T> int[] updateList(String id, List<T> list){
        try {
            MappedStatement ms = sqlSessionTemplate.getConfiguration().getMappedStatement(id);
            SqlCommandType sqlCommandType = ms.getSqlCommandType();
            //获取绑定sql
            BoundSql boundSql = ms.getSqlSource().getBoundSql(list.get(0));
            //绑定的sql语句
            String sql = boundSql.getSql();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            Connection connection = sqlSessionTemplate.getConnection();
            PreparedStatement statement  = null;
            //获取statement
            if(sqlCommandType == SqlCommandType.INSERT){
                statement = connection.prepareStatement( sql , Statement.RETURN_GENERATED_KEYS);
            }else{ statement = connection.prepareStatement(sql);}

            for (T item : list) {

                if (Objects.isNull(item)) continue;

                if(item instanceof Map){
                    @SuppressWarnings("unchecked")
                    Map<String, Object> itemMap = (Map<String, Object>) item;
                    for (int index = 0; index < parameterMappings.size(); index++) {
                        ParameterMapping pm = parameterMappings.get(index);
                        //设置参数 索引
                        statement.setObject(index + 1 , itemMap.get(pm.getProperty()));
                    }

                }else if(item instanceof Long || item instanceof String || item instanceof Integer){
                    statement.setObject(1  , item);
                }else{
                    for (int index = 0; index < parameterMappings.size(); index++) {
                        ParameterMapping pm = parameterMappings.get(index);
                        //拼接  实体  get 方法名   反射执行
                        String methodName = StringUtils.substring(pm.getProperty() , 0);
                        Method  method = item.getClass().getMethod(methodName);
                        //执行  获取 实体属性值
                        Object value = method.invoke(item);
                        statement.setObject(index  + 1 , value);
                    }
                }

                //添加执行
                statement.addBatch();
                //执行
                return statement.executeBatch();
            }
        } catch (SQLException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    //新增
    @Override
    public <T> int insert(String id, T params){
        return  sqlSessionTemplate.insert(id ,params);
    }

    //新增 集合
    @Override
    public <T> int[] insertList(String id, List<T> list){
        return updateList(id , list);
    }

    //删除
    @Override
    public <T>  int delete(String id, T params){
        return sqlSessionTemplate.delete(id , params);
    }

    //删除集合
    @Override
    public <T> int[] deleteList(String id, List<T> list){
        return  updateList(id , list);
    }

    //打印 sql 语句 调试用
    @Override
    public <T> void printSql(String id , T params){
        try {
            MappedStatement ms = sqlSessionTemplate.getConfiguration().getMappedStatement(id);
            BoundSql boundSql = ms.getSqlSource().getBoundSql(params);
            String sql = boundSql.getSql();
            sql = sql.replaceAll("\\n", "");
            sql = sql.replaceAll("\\t", "");
            sql = sql.replaceAll("[[ ]]{2,}", " ");
            List<ParameterMapping> list2 = boundSql.getParameterMappings();
            if (params == null) {

            } else if (params instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) params;
                for (int index = 0; index < list2.size(); index++) {
                    ParameterMapping pm = list2.get(index);
                    Object value = map.get(pm.getProperty());
                    sql = sql.replaceFirst("[?]", value + "");
                }
            } else if (params instanceof Long || params instanceof String || params instanceof Integer) {
                sql = sql.replaceFirst("[?]", params + "");
            } else {
                for (ParameterMapping pm : list2) {
                    String methodName = StringUtils.substring("get_" + pm.getProperty(), 1);
                    Method method = params.getClass().getMethod(methodName);
                    Object value = method.invoke(params);
                    sql = sql.replaceFirst("[?]", value + "");
                }
            }
            LoggerFactory.getLogger("Sql").info(sql);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
